import {
  ContextType,
  HandleType,
  RouteTargetType,
  RouterRegOptionType,
} from "../types";
import { Router } from "../core/router";
import { METHOD_KEY_ROUTE } from "./route";
import { CLASS_KEY_CONTROLLER } from "./controller";
const { readdirSync, statSync } = require("node:fs");
const { resolve } = require("node:path");

/**控制器原型属性类型 */
type ControllerType = Required<RouterRegOptionType & { prefix: string }>;

/**路由请求原型属性类型 */
type RouteType = Required<RouteTargetType>[];

/**
 * 将原型链上函数注册到路由表
 * 
 * @param {Router} router 路由对象
 * @param {Object} target 控制器原型链
 */
function registerRoutes(router: Router, target: any) {
  const controller: ControllerType = Reflect.get(target, CLASS_KEY_CONTROLLER);
  if (!controller) {
    return;
  }
  const routes: RouteType = Reflect.get(target.prototype, METHOD_KEY_ROUTE);
  if (Array.isArray(routes) && routes.length > 0) {
    for (const route of routes) {
      // 合并控制器参数
      const { ignoreGlobalPrefix, middleware } = controller;
      const _ignoreGlobalPrefix =
        ignoreGlobalPrefix || route.ignoreGlobalPrefix;
      const _middleware = middleware.concat(route.middleware);

      // 路由注册必要参数 method、path、handle
      const method = route.method.toUpperCase();
      const path = controller.prefix + route.path;
      const handle = async (ctx: ContextType) => {
        const body = await route.handle(ctx);
        if (body) {
          ctx.body = body;
        }
      };

      // 进行注册到路由表
      router.register(method, path, handle, {
        middleware: _middleware,
        ignoreGlobalPrefix: _ignoreGlobalPrefix,
      });
    }
  }
}

/**
 * 递归扫描目录文件
 *
 * @param {String} rootPaths 控制器所在目录路径
 * @param {Router} router 路由对象
 */
function controllerRecursive(rootPaths: string, router: Router) {
  try {
    const files = readdirSync(rootPaths);
    for (const file of files) {
      const filePath = resolve(rootPaths, file);
      const fileStat = statSync(filePath);
      // 文件目录进行递归再次require
      if (fileStat.isDirectory()) {
        controllerRecursive(filePath, router);
      } else if (file.endsWith(".js") || file.endsWith(".ts")) {
        // 扫描带有 .js 或 .ts 后缀的文件
        let mod: { __esModule: boolean; name?: string };
        try {
          mod = require(filePath);
        } catch (error: any) {
          console.error(`Failed require "${filePath}": ${error.message}`);
          continue;
        }
        // 根据导出方式获取原型链
        let target: unknown;
        if (Reflect.has(mod, CLASS_KEY_CONTROLLER)) {
          target = mod;
        } else if (mod.__esModule && "default" in mod) {
          target = mod.default;
        } else {
          target = mod.constructor;
        }
        // 进行注册
        registerRoutes(router, target);
      }
    }
  } catch (error: any) {
    console.error(`Failed readdirSync "${rootPaths}": ${error.message}`);
  }
}

/**
 * 扫描控制器路由添加到路由表
 *
 * 根据所在目录扫描所有js和ts文件
 *
 * 文件class导出方式：
 * - export default class xxx {}
 * - module.exports = new xxx();
 * - module.exports = xxx;
 * @param {String} controllerPath 控制器所在目录
 * @param {Router} router 路由对象
 * @returns {HandleType} router.routes() 路由中间件
 * @example
 * ```javascript
 * const app = new Application();
 * const router = new Router();
 *
 * // 控制器目录
 * const controllerPath = resolve(__dirname, "../controller");
 *
 * // 使用已有router
 * app.use(ScanController(controllerPath, router));
 *
 * // 默认初始router
 * app.use(ScanController(controllerPath));
 *
 * ```
 */
export function ScanController(
  controllerPath: string,
  router?: Router
): HandleType {
  // 是否初始路由对象
  router = router || new Router();
  // 递归扫描目录下所有js和ts文件
  controllerRecursive(controllerPath, router);
  // 返回路由表中间件
  return router.routes();
}
